/* Andreas Baum
 * ServerThread.java
 * Handles the interaction between the login server and a client
 * CS 78 - Networks
 */

package server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ServerThread extends Thread {
	private Socket socket = null;
	private PrintWriter out = null;
	private BufferedReader in = null;
	private String username;
	private String address;
	ConnectionManager cm;

	
	public ServerThread(Socket socket) {
		super("ServerThread");
		this.socket = socket;
		
		this.address = this.socket.getInetAddress().getHostName();
		
		// get the connection manager, which handles all the 
		// information about current users, and the interaction 
		// between users
		cm = ConnectionManager.getConnectionManager();

		
		try {
		
			out = new PrintWriter(socket.getOutputStream(), true);
			in = new BufferedReader(
					new InputStreamReader(socket.getInputStream()));

			
			// Receive the username from the client connecting
			// only accept the first word and trim white space:
			username = in.readLine().split(" ")[1];
			
			
			// Check if username is already used:
			if (!cm.doesNameExist(username)) {
				// If username is not used, inform server of login.
				System.out.println(username + " (" + address + ") has joined the server.");
				// Update user list, and inform other users:
				cm.logInUser(username, this);
				// Inform user of successful connection:
				this.info("You are now logged in as " + username + ". Type ? for help.");
				// Start thread to listen to the socket:
				this.start();
			}
			else {
				// If username was already taken, inform server and user:
				System.out.println("Warning: A user tried to log in as " + username +", but name is already used.");
				this.err("Username already exists. Disconnecting...");
				try {
					out.println("EXT");
					out.flush();
					// Inform server that user was disconnected.
					System.out.println("User posing as " + username + " was removed.");
					// Close streams and socket, as this thread is no longer needed:
					out.close();
					in.close();
					this.socket.close();
				} catch (IOException e) {
					e.printStackTrace();
					System.exit(1);
				}
			}

		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}



	}

	public void run() {

		String line = null;

		try {				
			
			// when the socket receives something:
			while ( ( ( line = in.readLine() ) != null ) && !socket.isClosed(  ) ) {

				// Remove white space at beginning and end of user entry:
				line = line.trim();
				
				// if the user just hit enter, do nothing, 
				// wait for next input
				if (line.equals(""))
					continue;
				
				// If the user issued a keyword command, it is the first word:
				String command = line.split(" ")[0].toUpperCase();
				
				// If the user entered only a single-word command, 
				// check for keyword matches:
				if (line.toUpperCase().equals(command)) {
					
					// If the user requested the help file:
					if ((command.equals("?")) || (command.equals("HELP"))) {
						this.help();
						continue;
					}
					
					// If the user has requested the buddy list:
					if ((command.equals("GET")) || (command.equals("WHO"))) {
						Object[] buddies = cm.getBuddyList();
						int i = 0;
						this.info("Users currently connected:");
						while (i < buddies.length) {
							String buddylist = (String) buddies[i] + " ";
							i++;
							// only put four users per line:
							while ((i % 4 != 0) && (i<buddies.length)){
								buddylist = buddylist.concat(((String) buddies[i]) + " ");
								i++;
							}
							// send a line of users:
							this.info(buddylist);
							
						}
//						// inform server that user requested the buddy list: 
						System.out.println(username + " requested the buddy list.");
						line = null;
						continue;
					}
					
					// If the user has requested to leave the server:
					if ((command.equals("-EXT") || (command.equals("-QUIT"))) 
							|| (command.equals("-EXIT"))) {
						this.disconnect();
						line = null;
						break;
					}

				}
				
				if (command.equals("CONNECT")) {
					if (line.split(" ")[1].equals(username)){
						this.info("Can't connect to yourself!");
						continue;
					}
					if (!cm.doesNameExist(line.split(" ")[1])) {
						this.info(line.split(" ")[1] + " is not a currently connected user.");
						continue;
					}
					cm.startServer(line.split(" ")[1], username);
					continue;
				}
				
				if (command.equals("STARTED")) {
					cm.startClient(line.split(" ")[1], username);
					continue;
				}
				
				if (command.equals("CONNECTED")) {
					String serverName = line.split(" ")[1];
					System.out.println(username + " can now read the Music library of " + serverName + ".");
					continue;
				}
			}
		} catch (Exception e) {
			try {
				// if error, log the user out and inform server:
				System.out.println(username + " has disconnected.");
				cm.logOutUser(username);
				in.close();
				out.close();
				this.socket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		} 
		// If socket is closed, but the user is still in the user list
		// log the user out and inform the server that the user has been 
		// disconnected:
		if (cm.doesNameExist(username)){
			System.out.println(username + " has disconnected.");
			cm.logOutUser(username);
		}
	}

	/** Sends an error message */
	public void err(String string) throws IOException{
		System.out.println("<<ERROR: " + string + ">>");
		out.println("ERR <<ERROR: " + string + ">>");
		out.flush();	
	}
	
	/** Sends an info message */
	public void info(String string) throws IOException{
		out.println("PRINT :: " + string);
		out.flush();
	}

	/** This method sends the user the help file, if he requests it */
	public void help() throws IOException {
		System.out.println(username + " requested help.");
		this.info("get or who \tto get user list");
		this.info("connect <username>\tto connect to another user");
		this.info("? \t\t\tto see this message again!");
	}
	
	/** This method tells a user to start his server side proxy */
	public void startServerConnector(String clientName) {
		System.out.println("Sending startup order to " + username + " coming from " + clientName + ".");
		out.println("SERVER " + clientName);
		out.flush();
	}
	
	/** Tells a user to start his client side proxy */
	public void startClientConnector(String serverName, String serverAddress) {
		out.println("CLIENT " + serverName + " " + serverAddress);
		out.flush();
	}
	
	/** Returns the IP address of this user */
	public String getAddress(){
		return this.address;
	}
	
	/** Disconnects the user after sending feedback to the server */
	public void disconnect() {
		try {
			out.println("EXT");
			// Update the user list:
			cm.logOutUser(username);
			out.flush();
			// Send feedback to server:
			System.out.println(username + " just logged out.");
			// Close all streams and socket:
			out.close();
			in.close();
			this.socket.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

}